package com.mjk.common.web.getway.utils.loadbalancer;

import cn.hutool.core.thread.NamedThreadFactory;
import com.mjk.common.core.redis.datatype.RedisHashUtils;
import com.mjk.common.tools.gson.GsonUtils;
import com.mjk.common.tools.monitor.IMonitor;
import com.mjk.common.tools.springextend.BeeApplicationListener;
import com.mjk.common.util.ComputerUtils;
import com.mjk.common.util.ObjectUtils;
import lombok.Data;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 负载均衡接口类
 */
public abstract class AbstractLoadBalancer implements BeeApplicationListener {
    public final String EVN_KEY = "evn";
    /**
     * 每个客户端 ip 可以请求的服务器地址
     */
    protected Map<String, List<String>> servers;
    /**
     * 每个环境 对应的轮训ID
     */
    protected Map<String,Integer> currentIndex;
    /**
     * 客户端IP到环境ID的映射
     */
    protected Map<String,String> ip2EvnMap;
    /**
     * 定时任务线程池核心线程数
     */
    private static final int CORE_POOL_SIZE = 2;
    /**
     * 线程池 用于定时获取 服务信息
     */
    protected ScheduledThreadPoolExecutor pollExecutor;

    public AbstractLoadBalancer(){
        ThreadFactory namedThreadFactory = new NamedThreadFactory("load-balance-", false);
        pollExecutor = new ScheduledThreadPoolExecutor(CORE_POOL_SIZE, namedThreadFactory, new ThreadPoolExecutor.DiscardOldestPolicy());
    }
    /**
     * 返回负载均衡后的服务器IP 端口信息
     * 如果请求IP地址返回是空，则直接调用本jvm内的请求
     * @param key
     * @return
     */
    public abstract  String getNextServer(String key);
    @Override
    public void doApplicationEvent(ContextRefreshedEvent event) {
        IMonitor server = event.getApplicationContext().getBean(IMonitor.class);
        RedisTemplate redisTemplate = event.getApplicationContext().getBean("redisTemplate", RedisTemplate.class);
        pollExecutor.scheduleAtFixedRate(
                () -> {
                    List<ComputerUtils.ComputerInfo> infos = server.getServices(null);
                    //查询环境信息
                    List<Object> evns = RedisHashUtils.getValues(redisTemplate,EVN_KEY);
                    if(!CollectionUtils.isEmpty(evns)){
                        evns.forEach(item->{
                            Evn evn = GsonUtils.json2Bean(GsonUtils.bean2Json(item),Evn.class);
                            if(ObjectUtils.isNotEmpty(evn.getClientIp())){
                                String[] cilentIps = evn.getClientIp().split(",");
                                List<String> serverIps = ObjectUtils.isEmpty(evn.getServerIp())?new ArrayList<>(): Arrays.asList(evn.getServerIp().split(","));
                                //比较配置的服务列表和获取的服务接口
                                List<String> ips =infos.stream().filter(item1-> serverIps.contains(item1.getIp()+":"+item1.getPort())).map(item1->item1.getIp()+":"+item1.getPort()).toList();
                                Arrays.asList(cilentIps).stream().forEach(item2->{
                                    //每个IP 对应的服务器地址
                                    servers.put(item2,ips);
                                    //每个IP对应的环境ID 用于获取每个环境的index
                                    ip2EvnMap.put(item2,evn.getEvnId()+"");
                                });
                            }
                            if(!currentIndex.containsKey(evn.getEvnId()+""))
                                currentIndex.put(evn.getEvnId()+"",0);
                        });
                    }
                },
                5l, 5l, TimeUnit.SECONDS
        );
    }
    @Data
    public class Evn implements Serializable {

        private Integer evnId;

        private String evnName;

        private String evnDescribe;

        private String enable;

        private String clientIp;

        private String serverIp;

        private String softLoadPath;
    }
}
